package com.css.fxfzdzzh.util;

import com.css.fxfzdzzh.config.property.FxfzConfig;
import com.css.fxfzdzzh.constants.FxfzConstants;
import io.jsonwebtoken.*;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @Author wenyugang
 * @Date 2020/4/26
 * @Version 1.0.0
 * @Description
 */
public class PlatformJwtUtils {

    private static FxfzConfig fxfzConfig;

    static {
        if (fxfzConfig == null) {
            fxfzConfig = PlatformSpringUtils.getBean(FxfzConfig.class);
        }
    }

    /**
     * 由字符串生成加密key
     * @return
     */

    private static Key generateKey(){
        String secretKey = FxfzConstants.secretKey;
        //签名算法，选择SHA-256
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        //将BASE64SECRET常量字符串使用base64解码成字节数组
        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(secretKey);
        //使用HmacSHA256签名算法生成一个HS256的签名秘钥Key
        Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
        return signingKey;
    }

    public static String createToken(String userId){
        long expireTime = fxfzConfig.getExpireTime();
        return generateJwt(userId,expireTime);
    }


    /**
     * @Description: 生成JWT字符串
     * 格式：A.B.C
     * A-header头信息
     * B-payload 有效负荷
     * C-signature 签名信息 是将header和payload进行加密生成的
     * @param userId - 用户编号
     */

    private  static String generateJwt(String userId, long expireTime){
        if (PlatformObjectUtils.isEmpty(userId)) {
            throw new IllegalArgumentException("Cannot create JWT Token without username");
        }
        if (expireTime < 0) {
            throw new IllegalArgumentException("过期时间不合法");
        }
        //获取当前系统时间-生成JWT的时间
        long nowTimeMillis = System.currentTimeMillis();

        //过期时间
        Date expDate = new Date(nowTimeMillis + expireTime);

        //设置头部信息
        Map<String,Object> headMap = new HashMap<>();
        headMap.put("alg", SignatureAlgorithm.HS256.getValue());
        headMap.put("type", "JWT");

        JwtBuilder builder = Jwts.builder()
                .setHeader(headMap)
                .setId(UUID.randomUUID().toString())  //设置jti(JWT ID)：是JWT的唯一标识，根据业务需要，这个可以设置为一个不重复的值，主要用来作为一次性token,从而回避重放攻击。
                .setIssuedAt(new Date(nowTimeMillis))  //签发时间
                .setSubject(userId)  //代表这个JWT的主体，即它的所有人，这个是一个json格式的字符串，可以存放什么userid，roldid之类的，作为什么用户的唯一标志。
                .signWith(SignatureAlgorithm.HS256, generateKey());  //设置签名使用的签名算法和签名使用的秘钥
        //设置过期时间
        if (expireTime >= 0){
            builder.setExpiration(expDate);
        }

        return builder.compact();
    }

    /**
     * 获取用户从Token中
     */
    public static String getUserFromToken(String token) {
        return getClaimFromToken(token).getSubject();
    }

    /**
     * <pre>
     *  验证token是否失效
     *  true:过期   false:没过期
     * </pre>
     */
    public static Boolean isTokenExpired(String token) {
        try {
            final Date expiration = getExpirationDateFromToken(token);
            return expiration.before(new Date());
        } catch (ExpiredJwtException expiredJwtException) {
            return true;
        }
    }

    /**
     * 获取jwt失效时间
     */
    public static Date getExpirationDateFromToken(String token) {
        return getClaimFromToken(token).getExpiration();
    }

    /**
     * 获取jwt的payload部分
     */
    private static Claims getClaimFromToken(String token) {
        return Jwts.parser()   //得到DefaultJwtParser
                .setSigningKey(generateKey())  //设置签名的秘钥
                .parseClaimsJws(token)
                .getBody();
    }

    public static boolean isJwtValid(String token) {
        try {
            Jwts.parser()   //得到DefaultJwtParser
                .setSigningKey(generateKey())  //设置签名的秘钥
                .parseClaimsJws(token)
                .getBody();
            return true;
        }catch (Exception e){
            return false;
        }
    }
}
